﻿using System.Collections.Immutable;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService.Symbols;

/// <summary>
///     预处理宏定义符号，可以是参数宏或无参数宏。
/// </summary>
public sealed class MacroSymbol : AbstractSymbol
{
    private readonly List<Macro> _declarations;
    private List<MacroParameterSymbol>? _parameters;

    internal MacroSymbol(Macro declaration) :
        base(SymbolKinds.Macro)
    {
        Name = declaration.Name;
        IsParameterMacro = declaration.IsParametered;
        _declarations = [declaration];
        LocationMap.AddLocation(declaration.NameIdentifier.GetLocation());
    }


    private MacroSymbol(string name) : base(SymbolKinds.Macro)
    {
        Name = name;
        IsParameterMacro = false;
        _declarations = [];
        IsPredefined = true;
    }


    public override string Name { get; }

    public override bool IsMacro => true;

    public override bool CanMakeArray => false;

    public override bool IsParameter => true;


    /// <summary>
    ///     如果此宏符号是预定义的，返回<see langword="true" />
    /// </summary>
    public bool IsPredefined { get; }


    /// <summary>
    ///     如果是参数宏，返回<see langword="true" />
    /// </summary>
    public bool IsParameterMacro { get; }


    /// <summary>
    ///     参数宏的参数个数
    /// </summary>
    public int ParametersCount => _parameters?.Count ?? 0;


    /// <summary>
    ///     创建一个预定义宏符号。
    /// </summary>
    /// <param name="name">预定义宏名</param>
    public static MacroSymbol PreDefine(string name)
    {
        return new MacroSymbol(name);
    }


    /// <summary>
    ///     如果可以接受指定个数的参数，返回<see langword="true" />
    /// </summary>
    public bool CanAcceptParametersLength(int length)
    {
        return length == _parameters?.Count ||
               (_parameters?.Count > 0 && _parameters[^1].IsVaArgs && length > _parameters.Count);
    }


    internal bool TryAddParameter(MacroParameterSymbol parameterSymbol)
    {
        if (_parameters?.Exists(p => p.Name == parameterSymbol.Name) == true)
        {
            return false;
        }

        _parameters ??= [];
        _parameters.Add(parameterSymbol);
        return true;
    }


    /// <summary>
    ///     获取宏的参数列表
    /// </summary>
    public ImmutableArray<MacroParameterSymbol> GetParameters()
    {
        return _parameters?.ToImmutableArray() ?? ImmutableArray<MacroParameterSymbol>.Empty;
    }


    /// <summary>
    ///     查找指定名称的参数符号
    /// </summary>
    public MacroParameterSymbol? GetParameter(string name)
    {
        return _parameters?.Find(p => p.Name == name);
    }


    /// <summary>
    ///     获取指定索引的参数符号。
    /// </summary>
    /// <param name="index">参数索引</param>
    public MacroParameterSymbol? GetParameter(int index)
    {
        if (_parameters == null || _parameters.Count == 0 || index < 0)
        {
            return null;
        }

        if (index < _parameters.Count)
        {
            return _parameters[index];
        }

        MacroParameterSymbol last = _parameters[^1];
        return last.IsVaArgs ? last : null;
    }


    /// <summary>
    ///     获取第一个宏定义
    /// </summary>
    public Macro GetFirstDeclaration()
    {
        return _declarations[0];
    }

    /// <summary>
    ///     获取最后一个宏定义
    /// </summary>
    public Macro GetLastDeclaration()
    {
        return _declarations[^1];
    }

    /// <summary>
    ///     获取包含所有定义的列表
    /// </summary>
    public ImmutableArray<Macro> GetDeclarations()
    {
        return [.. _declarations];
    }


    /// <summary>
    ///     添加一个新的宏定义声明语法
    /// </summary>
    public override bool Redeclare(ISymbol symbol)
    {
        // 如果是预定义的符号，直接返回true.
        if (IsPredefined)
        {
            return true;
        }

        if (IsParameter || symbol is not MacroSymbol macro)
        {
            return false;
        }

        LocationMap.AddLocations(macro.GetLocations());
        _declarations.AddRange(macro.GetDeclarations());
        return true;
    }


    /// <summary>
    ///     在指定位置取消当前宏的声明
    /// </summary>
    public void CancelDeclaration(SyntaxLocation location)
    {
        LocationMap.AddLocation(SyntaxLocation.Create(
            location.Uri,
            location.Start,
            location.Width,
            location.IsComment,
            false));
    }
}